/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.evaluation;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.common.constant.evaluation.PwdRaCondition;
import cn.ac.nercis.pes.common.constant.evaluation.PwdRkCondition;
import cn.ac.nercis.pes.common.constant.knowledge.*;
import cn.ac.nercis.pes.common.utils.ConvertUtils;
import cn.ac.nercis.pes.common.utils.DataUtils;
import cn.ac.nercis.pes.common.constant.evaluation.EvaluationConclusion;
import cn.ac.nercis.pes.common.constant.evaluation.EvaluationResult;
import cn.ac.nercis.pes.common.constant.project.FinishState;
import cn.ac.nercis.pes.common.constant.project.RefTargetType;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.model.dto.common.TreeNode;
import cn.ac.nercis.pes.model.dto.evaluation.onsite.AssetEvalProcessDTO;
import cn.ac.nercis.pes.model.dto.evaluation.onsite.EvaluationRecordDTO;
import cn.ac.nercis.pes.model.dto.evaluation.onsite.LayerTargetEvalProcessDTO;
import cn.ac.nercis.pes.model.dto.evaluation.onsite.UnitTargetEvalDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationRecordRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.ResultCorrectionRepository;
import cn.ac.nercis.pes.repository.dal.knowledge.KmEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemSpecialTargetRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.mapper.evaluation.EvaluationRecordMapper;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.repository.model.evaluation.AssetEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRecordDO;
import cn.ac.nercis.pes.repository.model.evaluation.ResultCorrectionDO;
import cn.ac.nercis.pes.repository.model.knowledge.KmEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.project.SystemSpecialTargetDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.repository.utils.DbUtils;
import cn.ac.nercis.pes.service.manage.ProcessRelatedService;
import cn.ac.nercis.pes.service.utils.AssetUtils;
import cn.ac.nercis.pes.service.utils.CalculateUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 测评记录服务
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record EvaluationRecordService(AssetRepository assetRepository,
                                      SystemTargetRepository systemTargetRepository,
                                      SystemSpecialTargetRepository systemSpecialTargetRepository,
                                      AssetEvaluationMethodRepository evaluationMethodRepository,
                                      EvaluationRecordRepository evaluationRecordRepository,
                                      ResultCorrectionRepository resultCorrectionRepository,
                                      ProcessRelatedService processRelatedService,
                                      KmEvaluationMethodRepository kmEvaluationMethodRepository) {

    @Autowired
    public EvaluationRecordService {
    }

    /**
     * 获取资产树节点
     * 第一级节点：资产分类信息
     * 第二级节点：资产对象信息
     *
     * @param systemId 系统ID
     * @return 资产树节点列表
     */
    public Result<List<TreeNode>> findAssetTree(@NonNull String systemId) {
        boolean existSystemTarget = systemTargetRepository.existsBySystemId(systemId);
        if (!existSystemTarget) {
            return Result.failed(ResultCode.EMPTY_SYSTEM_TARGET_DATA);
        }
        List<AssetDO> assetDOS = assetRepository.findAllBySystemId(systemId);
        if (CollectionUtils.isEmpty(assetDOS)) {
            return Result.failed(ResultCode.EMPTY_SYSTEM_ASSET_DATA);
        }
        List<AssetEvaluationMethodDO> evaluationMethods = evaluationMethodRepository.findAllBySystemId(systemId);
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemId(systemId);
        List<TreeNode> nodes = Arrays.stream(VirtualClassification.values())
                .map(ac -> TreeNode.builder()
                        .id(ac.getId())
                        .name(ac.getName())
                        .children(findAssetTree(ac, assetDOS, evaluationMethods, evaluationRecords))
                        .build())
                .collect(Collectors.toList());
        return Result.success(nodes);
    }

    /**
     * 获取资产对象节点信息
     *
     * @param systemId 系统ID
     * @param assetId  资产对象ID
     * @return 资产对象节点信息
     */
    public Result<TreeNode> getAssetTreeNode(@NonNull String systemId, @NonNull String assetId) {
        AssetDO assetDO = assetRepository.findById(assetId).orElse(null);
        if (Objects.isNull(assetDO)) {
            return Result.failed(ResultCode.ASSET_NOT_EXIST);
        }
        if(StringUtils.equals(AssetClassification.PHY_ENV.getId(),assetDO.getClassId())){
            var evaluationMethods = evaluationMethodRepository.findAllBySystemId(systemId);
            var evaluationRecords = evaluationRecordRepository.findAllBySystemId(systemId);
            var subAssets = this.assetRepository.findAllBySystemIdAndQuoteAssetId(systemId,assetDO.getId());
            var psAssets = findAssetTree(assetDO.getId(),List.of(AssetClassification.PHY_SECURITY.getId()),subAssets,evaluationMethods,evaluationRecords);
            return Result.success(TreeNode.builder()
                    .id(assetDO.getId())
                    .name(AssetUtils.getAssetName(assetDO))
                    .data(getAssetEvalProcess(assetDO, psAssets))
                    .children(psAssets)
                    .build());
        }else if(StringUtils.equals(AssetClassification.KEY_BUSINESS.getId(),assetDO.getClassId())){
            var assetClassIds = List.of(AssetClassification.SUB_BUSINESS.getId(),AssetClassification.KEY_DATA.getId());
            var evaluationMethods = evaluationMethodRepository.findAllBySystemId(systemId);
            var evaluationRecords = evaluationRecordRepository.findAllBySystemId(systemId);
            var subAssets = assetRepository.findAllBySystemId(systemId);
            var psAssets = findAssetTree(assetDO.getId(),assetClassIds,subAssets,evaluationMethods,evaluationRecords);
            return Result.success(TreeNode.builder()
                    .id(assetDO.getId())
                    .name(AssetUtils.getAssetName(assetDO))
                    .data(getAssetEvalProcess(assetDO, psAssets))
                    .children(psAssets)
                    .build());
        }else{
            var evaluationMethods = evaluationMethodRepository.findAllBySystemIdAndAssetId(systemId, assetId);
            var evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(systemId,AssetDO.builder().id(assetId).build());
            return Optional.of(assetDO)
                    .map(a -> TreeNode.builder()
                            .id(assetId)
                            .name(AssetUtils.getAssetName(a))
                            .data(getAssetEvalProcess(a, evaluationMethods, evaluationRecords))
                            .build())
                    .map(Result::success)
                    .orElse(Result.failed());
        }
    }

    /**
     * 获取资产对象测评进度信息
     * @param systemId 系统ID
     * @param assetId  资产对象ID
     * @return 资产对象测评进度信息
     */
    public Result<AssetEvalProcessDTO> getAssetEvalProcess(@NonNull String systemId, @NonNull String assetId){
        AssetDO assetDO = assetRepository.findById(assetId).orElse(null);
        if (Objects.isNull(assetDO)) {
            return Result.failed(ResultCode.ASSET_NOT_EXIST);
        }
        var evaluationMethods = evaluationMethodRepository.findAllBySystemIdAndAssetId(systemId, assetId);
        var evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(systemId,
                AssetDO.builder().id(assetId).build());
        return Result.success(getAssetEvalProcess(assetDO, evaluationMethods, evaluationRecords));
    }

    /**
     * 获取资产对象关联的单元指标测评信息
     *
     * @param systemId      系统ID
     * @param assetId       资产对象ID
     * @param layerTargetId 层面指标ID
     * @return 单元指标测评信息列表
     */
    public List<UnitTargetEvalDTO> findUnitTargetByAsset(@NonNull String systemId, @NonNull String assetId,
                                                         @NonNull String layerTargetId) {
        this.repairDuplicateData(systemId,assetId);
        var evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(systemId,
                AssetDO.builder().id(assetId).build());
        var unitTargetIds = evaluationMethodRepository.findAllBySystemIdAndAssetId(systemId, assetId)
                .stream()
                .filter(em -> StringUtils.equals(em.getLayerTargetId(), layerTargetId))
                .map(AssetEvaluationMethodDO::getUnitTargetId)
                .collect(Collectors.toSet());
        var unitTargets= systemTargetRepository.findAllBySystemIdAndKmTargetIdIn(systemId,unitTargetIds)
                .stream()
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .map(t -> UnitTargetEvalDTO.builder()
                        .systemId(systemId)
                        .assetId(assetId)
                        .unitTargetId(t.getKmTargetId())
                        .unitTargetName(t.getKmName())
                        .finishState(getUnitTargetFinishState(t, evaluationRecords))
                        .build())
                .toList();
        var unitSpecialTargets = systemSpecialTargetRepository.findAllById(unitTargetIds)
                .stream()
                .sorted(Comparator.comparingInt(SystemSpecialTargetDO::getSort))
                .map(t -> UnitTargetEvalDTO.builder()
                        .systemId(systemId)
                        .assetId(assetId)
                        .unitTargetId(t.getId())
                        .unitTargetName(t.getName())
                        .finishState(getUnitTargetFinishState(t, evaluationRecords))
                        .build())
                .toList();
        return Stream.concat(unitTargets.stream(),unitSpecialTargets.stream()).toList();
    }

    /**
     * 修复重复数据
     */
    public void repairDuplicateData(@NonNull String systemId, @NonNull String assetId){
        var evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(systemId,
                AssetDO.builder().id(assetId).build());
        var unitTargetIds = evaluationRecords.stream()
                .map(EvaluationRecordDO::getUnitTargetId)
                .collect(Collectors.toSet());
        List<EvaluationRecordDO> duplicateData = new ArrayList<>();
        for(var unitTargetId:unitTargetIds){
            var records = evaluationRecords.stream()
                    .filter(er->StringUtils.equals(er.getUnitTargetId(),unitTargetId))
                    .toList();
            duplicateData.addAll(DbUtils.findDuplicateData(records));
        }
        if(CollectionUtils.isNotEmpty(duplicateData)){
            this.evaluationRecordRepository.deleteAll(duplicateData);
        }
    }

    /**
     * 获取资产对象测评记录信息
     *
     * @param systemId     系统ID
     * @param assetId      资产对象ID
     * @param unitTargetId 单元指标ID
     * @return 资产对象测评记录信息
     */
    public Result<EvaluationRecordDTO> getAssetEvaluationRecord(@NonNull String systemId, @NonNull String assetId, @NonNull String unitTargetId) {
        var evaluationMethod = DataUtils.getFirst(evaluationMethodRepository.findBySystemIdAndAssetIdAndUnitTargetId(systemId, assetId, unitTargetId));
        if (Objects.isNull(evaluationMethod)) {
            return Result.failed(ResultCode.NOT_FOUND);
        }
        var specialTargets = systemSpecialTargetRepository.findAllBySystemIdAndLayerTargetId(systemId,evaluationMethod.getLayerTargetId());
        String targetDesc="";
        String notApplyReasons = "";
        boolean isApply = true;
        if(evaluationMethod.getRefType() == RefTargetType.STANDARD){
            var systemTarget = systemTargetRepository.findBySystemIdAndKmTargetId(systemId, unitTargetId).orElse(null);
            if (Objects.isNull(systemTarget)) {
                return Result.failed(ResultCode.SYSTEM_TARGET_NOT_EXIST);
            }
            isApply = systemTarget.getIsApply();
            notApplyReasons = systemTarget.getReasons();
            var sTarget = specialTargets.stream()
                    .filter(st->StringUtils.equals(st.getKmTargetId(),systemTarget.getKmTargetId()))
                    .findFirst();
            if(sTarget.isPresent()){
                targetDesc = sTarget.get().getTargetDesc();
            }else{
                targetDesc = systemTargetRepository.findAllBySystemIdAndKmParentId(systemId,unitTargetId)
                        .stream()
                        .findFirst()
                        .map(SystemTargetDO::getKmName)
                        .orElse("");
            }
        }else{
            var specialTarget = specialTargets.stream()
                    .filter(st->StringUtils.equals(st.getId(),unitTargetId))
                    .findFirst();
            if(specialTarget.isPresent()){
                targetDesc = specialTarget.get().getTargetDesc();
            }
        }
        EvaluationRecordDTO evaluationRecord = DataUtils.findFirst(evaluationRecordRepository.findByAssetEvaMethodId(evaluationMethod.getId()))
                .map(EvaluationRecordMapper.INSTANCE::toDTO)
                .orElse(EvaluationRecordDTO.builder()
                        .systemId(systemId)
                        .assetId(assetId)
                        .layerTargetId(evaluationMethod.getLayerTargetId())
                        .unitTargetId(unitTargetId)
                        .assetEvaMethodId(evaluationMethod.getId())
                        .build());
        evaluationRecord.setIsApply(isApply);
        evaluationRecord.setMethod(evaluationMethod.getMethod());
        evaluationRecord.setDesc(getEvalMethodDesc(evaluationMethod));
        evaluationRecord.setTargetDesc(targetDesc);
        evaluationRecord.setRefType(evaluationMethod.getRefType());
        if (!evaluationRecord.getIsApply() && StringUtils.isBlank(evaluationRecord.getId())) {
            evaluationRecord.setContent(notApplyReasons);
        }
        return Result.success(evaluationRecord);
    }

    private String getEvalMethodDesc(AssetEvaluationMethodDO methodDO) {
        if (StringUtils.isBlank(methodDO.getMethod())) {
            return methodDO.getDesc();
        }
        List<EvaluationMethod> methods = Stream.of(StringUtils.split(methodDO.getMethod(), BaseConstants.MULTI_SPLIT_CHAR))
                .map(ConvertUtils::toInteger)
                .map(EvaluationMethod::fromMethod)
                .toList();
        return kmEvaluationMethodRepository.findAllByUnitTargetId(methodDO.getUnitTargetId())
                .stream()
                .filter(em -> methods.stream().anyMatch(m -> StringUtils.contains(em.getMethod(), m.getValue())))
                .sorted(Comparator.comparingInt(KmEvaluationMethodDO::getSort))
                .map(KmEvaluationMethodDO::getContent)
                .collect(Collectors.joining(BaseConstants.NEW_LINE));

    }

    public Result<String> checkEvaluationConclusion(@NonNull EvaluationRecordDTO evaluationRecord) {
        SystemTargetDO systemTarget = systemTargetRepository.findBySystemIdAndKmTargetId(evaluationRecord.getSystemId(),
                evaluationRecord.getUnitTargetId()).orElse(null);
        EvaluationRecordDO entity = evaluationRecordRepository.findById(evaluationRecord.getId()).orElse(null);
        if (Objects.isNull(systemTarget) || Objects.isNull(entity)) {
            return Result.success();
        }
        if (systemTarget.getKmType() == TargetType.TECHNOLOGY) {
            technologyResultProcess(evaluationRecord, systemTarget);
            CalculateUtils.calculateTechnologyScore(evaluationRecord);
        } else {
            CalculateUtils.calculateManageScore(evaluationRecord);
        }
        if (entity.getConclusion() == evaluationRecord.getConclusion()) {
            return Result.success();
        }
        ResultCorrectionDO resultCorrectionDO = DataUtils.getFirst(resultCorrectionRepository.findBySystemIdAndRecord(entity.getSystemId(),
                EvaluationRecordDO.builder().id(entity.getId()).build()));
        if (Objects.nonNull(resultCorrectionDO)) {
            return Result.failed("当前对象已存在整体测评数据，是否继续进行修改保存？");
        } else {
            return Result.success();
        }
    }

    public synchronized Result<EvaluationRecordDTO> saveAssetEvaluationRecord(@NonNull EvaluationRecordDTO evaluationRecord) {
        AssetDO assetDO = assetRepository.findById(evaluationRecord.getAssetId()).orElse(null);
        if (Objects.isNull(assetDO)) {
            return Result.failed(ResultCode.ASSET_NOT_EXIST);
        }
        AssetEvaluationMethodDO evaluationMethodDO = evaluationMethodRepository.findById(evaluationRecord.getAssetEvaMethodId()).orElse(null);
        if (Objects.isNull(evaluationMethodDO)) {
            return Result.failed(ResultCode.ASSET_EVALUATION_METHOD_NOT_EXIST);
        }
        var entity =  getRecordAndRemoveRepeat(evaluationRecord.getAssetEvaMethodId());
        if (Objects.isNull(entity)) {
            return createAssetEvaluationRecord(evaluationRecord, assetDO, evaluationMethodDO);
        } else {
            return modifyAssetEvaluationRecord(entity,evaluationRecord, evaluationMethodDO);
        }
    }

    private EvaluationRecordDO getRecordAndRemoveRepeat(String assetEvaMethodId){
        var records = this.evaluationRecordRepository.findByAssetEvaMethodId(assetEvaMethodId);
        var firstRecord = records.stream().findFirst().orElse(null);
        if(Objects.nonNull(firstRecord) && records.size()>DataUtils.SINGLE_COUNT){
            var repeatData = records.stream()
                    .filter(r->!StringUtils.equals(r.getId(),firstRecord.getId()))
                    .toList();
            this.evaluationRecordRepository.deleteAll(repeatData);
        }
        return firstRecord;
    }

    public Result<String> setRecordFinishState(@NonNull String evaluationRecordId, @NonNull FinishState state) {
        EvaluationRecordDO entity = evaluationRecordRepository.findById(evaluationRecordId).orElse(null);
        if (Objects.isNull(entity)) {
            return Result.failed(ResultCode.EVALUATION_RECORD_NOT_EXIST);
        }
        entity.setFinishState(state);
        entity.setModifyTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(evaluationRecordRepository::save)
                .map(EvaluationRecordDO::getId)
                .map(Result::success)
                .orElse(Result.failed());
    }

    public Result<String> checkUnitTargetNotApply(@NonNull String systemId, @NonNull String unitTargetId) {
        var specialTarget = this.systemSpecialTargetRepository.existsById(unitTargetId);
        if(specialTarget){
            return Result.success();
        }
        var systemTarget = systemTargetRepository.findBySystemIdAndKmTargetId(systemId, unitTargetId)
                .orElse(null);
        if (Objects.isNull(systemTarget)) {
            return Result.failed(ResultCode.SYSTEM_TARGET_NOT_EXIST);
        }
        if (!systemTarget.getIsApply()) {
            return Result.success();
        }
        var assetIds = assetRepository.findAllBySystemId(systemId)
                .stream()
                .filter(a -> Objects.nonNull(a.getIsUse()) && a.getIsUse())
                .map(AssetDO::getId)
                .collect(Collectors.toSet());
        var evaluationMethods = evaluationMethodRepository.findBySystemIdAndUnitTargetId(systemId, unitTargetId)
                .stream()
                .toList();
        var evaluationMethodIds = evaluationMethods
                .stream()
                .filter(em -> assetIds.stream().anyMatch(id -> StringUtils.equals(id, em.getAssetId())))
                .map(AssetEvaluationMethodDO::getId)
                .collect(Collectors.toSet());
        var notApplyCount = evaluationRecordRepository.findAllByAssetEvaMethodIdIn(evaluationMethodIds)
                .stream()
                .filter(er -> er.getConclusion() == EvaluationConclusion.NOT_APPLICABLE)
                .count();
        if (evaluationMethodIds.size() == notApplyCount) {
            return Result.failed(String.format("系统检测到当前“%s”指标下所有测评对象都为不适用。", systemTarget.getKmName()));
        } else {
            return Result.success();
        }
    }

    public Result<List<LayerTargetEvalProcessDTO>> findLayerTargetProcess(@NonNull String systemId, @NonNull String assetId) {
        Set<String> layerTargetIds = evaluationMethodRepository.findAllBySystemIdAndAssetId(systemId, assetId)
                .stream()
                .map(AssetEvaluationMethodDO::getLayerTargetId)
                .collect(Collectors.toSet());
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(systemId,
                AssetDO.builder().id(assetId).build());
        List<LayerTargetEvalProcessDTO> layerTargetEvalProcess = layerTargetIds.stream()
                .map(targetId -> LayerTargetEvalProcessDTO.builder()
                        .assetId(assetId)
                        .layerTargetId(targetId)
                        .state(getLayerTargetProcess(systemId, assetId, targetId, evaluationRecords))
                        .build())
                .collect(Collectors.toList());
        return Result.success(layerTargetEvalProcess);
    }

    public Result<LayerTargetEvalProcessDTO> getLayerTargetProcess(@NonNull String systemId, @NonNull String assetId, @NonNull String layerTargetId) {
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(systemId,
                AssetDO.builder().id(assetId).build());
        return Result.success(LayerTargetEvalProcessDTO.builder()
                .assetId(assetId)
                .layerTargetId(layerTargetId)
                .state(getLayerTargetProcess(systemId, assetId, layerTargetId, evaluationRecords))
                .build());
    }

    private FinishState getLayerTargetProcess(String systemId, String assetId, String layerTargetId, List<EvaluationRecordDO> evaluationRecords) {
        List<EvaluationRecordDO> layerEvalRecords = evaluationRecords.stream()
                .filter(er -> StringUtils.equals(er.getLayerTargetId(), layerTargetId))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(layerEvalRecords)) {
            return FinishState.NONE;
        }
        long finishCount = layerEvalRecords.stream()
                .filter(er -> er.getFinishState() == FinishState.FINISH)
                .count();
        long count = evaluationMethodRepository.countAllBySystemIdAndAssetIdAndLayerTargetId(systemId, assetId, layerTargetId);
        return finishCount == count ? FinishState.FINISH : FinishState.INCOMPLETE;
    }

    private Result<EvaluationRecordDTO> createAssetEvaluationRecord(EvaluationRecordDTO evaluationRecord,AssetDO assetDO,
                                                                    AssetEvaluationMethodDO evaluationMethodDO) {
        AtomicBoolean isApply = new AtomicBoolean(true);
        if(evaluationMethodDO.getRefType()== RefTargetType.STANDARD){
            var unitTarget = systemTargetRepository.findBySystemIdAndKmTargetId(evaluationRecord.getSystemId(),evaluationMethodDO.getUnitTargetId()).orElse(null);
            if(Objects.isNull(unitTarget)){
                return Result.failed(ResultCode.SYSTEM_TARGET_NOT_EXIST);
            }
            isApply.set(unitTarget.getIsApply());
            calculateScore(evaluationRecord,unitTarget.getKmType(),unitTarget);
        }else{
            var layerTarget = systemTargetRepository.findBySystemIdAndKmTargetId(evaluationRecord.getSystemId(),evaluationMethodDO.getLayerTargetId()).orElse(null);
            if(Objects.isNull(layerTarget)){
                return Result.failed(ResultCode.SYSTEM_TARGET_NOT_EXIST);
            }
            calculateScore(evaluationRecord,layerTarget.getKmType(),null);
        }
        return Optional.of(EvaluationRecordMapper.INSTANCE.toDO(evaluationRecord))
                .map(er -> {
                    er.setFinishState(FinishState.INCOMPLETE);
                    er.setUnitTargetId(evaluationRecord.getUnitTargetId());
                    er.setRefType(evaluationRecord.getRefType());
                    er.setAsset(assetDO);
                    er.setCreateTime(LocalDateTime.now());
                    return er;
                })
                .map(evaluationRecordRepository::save)
                .map(entity -> {
                    EvaluationRecordDTO recordDTO = EvaluationRecordMapper.INSTANCE.toDTO(entity);
                    recordDTO.setIsApply(isApply.get());
                    recordDTO.setMethod(evaluationMethodDO.getMethod());
                    recordDTO.setDesc(getEvalMethodDesc(evaluationMethodDO));
                    recordDTO.setTargetDesc(evaluationRecord.getTargetDesc());
                    return recordDTO;
                })
                .map(Result::success)
                .orElse(Result.failed());
    }

    private void calculateScore(EvaluationRecordDTO evaluationRecord,TargetType targetType,SystemTargetDO systemTarget){
        if (targetType == TargetType.TECHNOLOGY) {
            technologyResultProcess(evaluationRecord, systemTarget);
            CalculateUtils.calculateTechnologyScore(evaluationRecord);
        } else {
            CalculateUtils.calculateManageScore(evaluationRecord);
        }
    }

    private Result<EvaluationRecordDTO> modifyAssetEvaluationRecord(EvaluationRecordDO entity,
                                                                    EvaluationRecordDTO evaluationRecord,
                                                                    AssetEvaluationMethodDO evaluationMethodDO) {
        if(evaluationMethodDO.getRefType()== RefTargetType.STANDARD){
            var unitTarget = systemTargetRepository.findBySystemIdAndKmTargetId(evaluationRecord.getSystemId(),evaluationMethodDO.getUnitTargetId()).orElse(null);
            if(Objects.isNull(unitTarget)){
                return Result.failed(ResultCode.SYSTEM_TARGET_NOT_EXIST);
            }
            calculateScore(evaluationRecord,unitTarget.getKmType(),unitTarget);
        }else{
            var layerTarget = systemTargetRepository.findBySystemIdAndKmTargetId(evaluationRecord.getSystemId(),evaluationMethodDO.getLayerTargetId()).orElse(null);
            if(Objects.isNull(layerTarget)){
                return Result.failed(ResultCode.SYSTEM_TARGET_NOT_EXIST);
            }
            calculateScore(evaluationRecord,layerTarget.getKmType(),null);
        }
        AtomicBoolean conclusionChange = new AtomicBoolean(false);
        if (entity.getConclusion() != evaluationRecord.getConclusion()) {
            conclusionChange.set(true);
            entity.setConclusion(evaluationRecord.getConclusion());
            processRelatedService.deleteEvalRecordRelatedData(entity);
            processRelatedService.resetEvalRecordRelateModuleProcess(entity);
        }
        entity.setConclusion(evaluationRecord.getConclusion());
        entity.setDResult(evaluationRecord.getDResult());
        entity.setAResult(evaluationRecord.getAResult());
        entity.setACondition(evaluationRecord.getACondition());
        entity.setKResult(evaluationRecord.getKResult());
        entity.setKCondition(evaluationRecord.getKCondition());

        entity.setDContent(evaluationRecord.getDContent());
        entity.setAContent(evaluationRecord.getAContent());
        entity.setKContent(evaluationRecord.getKContent());
        entity.setContent(evaluationRecord.getContent());

        entity.setDFiles(evaluationRecord.getDFiles());
        entity.setAFiles(evaluationRecord.getAFiles());
        entity.setKFiles(evaluationRecord.getKFiles());
        entity.setManageFiles(evaluationRecord.getManageFiles());

        entity.setScore(evaluationRecord.getScore());
        entity.setModifyTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(evaluationRecordRepository::save)
                .map(evaluationRecordDO -> {
                    if (conclusionChange.get()) {
                        processRelatedService.resetRiskAnalysis(entity.getSystemId(),entity.getUnitTargetId());
                    }
                    EvaluationRecordDTO recordDTO = EvaluationRecordMapper.INSTANCE.toDTO(evaluationRecordDO);
                    recordDTO.setIsApply(evaluationRecord.getIsApply());
                    recordDTO.setMethod(evaluationMethodDO.getMethod());
                    recordDTO.setDesc(getEvalMethodDesc(evaluationMethodDO));
                    recordDTO.setTargetDesc(evaluationRecord.getTargetDesc());
                    return recordDTO;
                })
                .map(Result::success)
                .orElse(Result.failed());
    }

    private void technologyResultProcess(EvaluationRecordDTO evaluationRecord, SystemTargetDO systemTarget) {
        if (Objects.nonNull(systemTarget) && !systemTarget.getIsApply()) {
            evaluationRecord.setDResult(EvaluationResult.NOT_APPLICABLE);
            evaluationRecord.setAResult(EvaluationResult.NOT_APPLICABLE);
            evaluationRecord.setACondition(PwdRaCondition.DEFAULT);
            evaluationRecord.setKResult(EvaluationResult.NOT_APPLICABLE);
            evaluationRecord.setKCondition(PwdRkCondition.DEFAULT);
            evaluationRecord.setDContent("");
            evaluationRecord.setAContent("");
            evaluationRecord.setKContent("");
            evaluationRecord.setDFiles("");
            evaluationRecord.setAFiles("");
            evaluationRecord.setKFiles("");
            return;
        }
        if (evaluationRecord.getDResult() != EvaluationResult.MATCH) {
            evaluationRecord.setAResult(evaluationRecord.getDResult());
            evaluationRecord.setACondition(PwdRaCondition.DEFAULT);
            evaluationRecord.setKResult(evaluationRecord.getDResult());
            evaluationRecord.setKCondition(PwdRkCondition.DEFAULT);
            evaluationRecord.setAContent("");
            evaluationRecord.setKContent("");
            evaluationRecord.setAFiles("");
            evaluationRecord.setKFiles("");
        }
    }

    public List<TreeNode> findAssetTree(VirtualClassification vc, List<AssetDO> assetDOS, List<AssetEvaluationMethodDO> evaluationMethods,
                                        List<EvaluationRecordDO> evaluationRecords) {
        switch (vc) {
            case PHY_SECURITY -> {
                return assetDOS.stream()
                        .filter(a -> StringUtils.equals(a.getClassId(), AssetClassification.PHY_ENV.getId()))
                        .map(a -> {
                            var psAssets = findAssetTree(a.getId(), List.of(AssetClassification.PHY_SECURITY.getId()), assetDOS, evaluationMethods, evaluationRecords);
                            if (CollectionUtils.isEmpty(psAssets)) {
                                return null;
                            }
                            return TreeNode.builder()
                                    .id(a.getId())
                                    .name(AssetUtils.getAssetName(a))
                                    .data(getAssetEvalProcess(a, psAssets))
                                    .children(psAssets)
                                    .build();
                        })
                        .filter(Objects::nonNull)
                        .toList();
            }
            case APP_SECURITY->{
                var assetClassIds = List.of(AssetClassification.SUB_BUSINESS.getId(),AssetClassification.KEY_DATA.getId());
                return assetDOS.stream()
                        .filter(a->StringUtils.equals(a.getClassId(), AssetClassification.KEY_BUSINESS.getId()))
                        .map(a->{
                            var subAssets = findAssetTree(a.getId(),assetClassIds,assetDOS,evaluationMethods,evaluationRecords);
                            if(CollectionUtils.isEmpty(subAssets)){
                                return null;
                            }
                            return TreeNode.builder()
                                    .id(a.getId())
                                    .name(AssetUtils.getAssetName(a))
                                    .data(getAssetEvalProcess(a, subAssets))
                                    .children(subAssets)
                                    .build();
                        })
                        .filter(Objects::nonNull)
                        .toList();
            }
            case SECURITY_MANAGE->{
                var manageAssetNames = Stream.of(LayerTarget.MANAGE_SYSTEM,LayerTarget.PERSONNEL_MANAGE,LayerTarget.BUILD_RUNNING,LayerTarget.EMERGENCY_HANDLING)
                        .map(LayerTarget::getName)
                        .toList();
                return assetDOS.stream()
                        .filter(a -> vc.getClassIds().stream().anyMatch(classId -> StringUtils.equals(a.getClassId(), classId)))
                        .filter(AssetDO::getIsUse)
                        .map(a -> {
                            var nodeName = manageAssetNames.stream().filter(man->StringUtils.startsWith(a.getName(),man)).findFirst().orElse(a.getName());
                            return TreeNode.builder()
                                    .id(a.getId())
                                    .name(nodeName)
                                    .data(getAssetEvalProcess(a, evaluationMethods, evaluationRecords))
                                    .build();
                        })
                        .collect(Collectors.toList());
            }
            default -> {
                return assetDOS.stream()
                        .filter(a -> vc.getClassIds().stream().anyMatch(classId -> StringUtils.equals(a.getClassId(), classId)))
                        .filter(AssetDO::getIsUse)
                        .map(a -> TreeNode.builder()
                                .id(a.getId())
                                .name(AssetUtils.getAssetName(a))
                                .data(getAssetEvalProcess(a, evaluationMethods, evaluationRecords))
                                .build())
                        .collect(Collectors.toList());
            }
        }
    }

    public List<TreeNode> findAssetTree(String parentAssetId, List<String> assetClassIds, List<AssetDO> assetDOS, List<AssetEvaluationMethodDO> evaluationMethods,
                                        List<EvaluationRecordDO> evaluationRecords) {
        return assetDOS.stream()
                .filter(a -> assetClassIds.stream().anyMatch(acId -> StringUtils.equals(acId, a.getClassId()))
                        && StringUtils.contains(a.getQuoteAssetId(), parentAssetId)
                        && a.getIsUse())
                .map(a -> TreeNode.builder()
                        .id(a.getId())
                        .name(a.getName())
                        .data(getAssetEvalProcess(a, evaluationMethods, evaluationRecords))
                        .build())
                .toList();
    }

    private AssetEvalProcessDTO getAssetEvalProcess(AssetDO assetDO, List<AssetEvaluationMethodDO> evaluationMethods,
                                                    List<EvaluationRecordDO> evaluationRecords) {
        long count = evaluationMethods.stream()
                .filter(em -> StringUtils.equals(em.getAssetId(), assetDO.getId()))
                .count();
        long finishCount = evaluationRecords.stream()
                .filter(er -> StringUtils.equals(er.getAsset().getId(), assetDO.getId())
                        && er.getFinishState() == FinishState.FINISH)
                .count();
        return AssetEvalProcessDTO.builder()
                .systemId(assetDO.getSystemId())
                .assetId(assetDO.getId())
                .assetName(AssetUtils.getAssetName(assetDO))
                .count(count)
                .finishCount(finishCount)
                .build();
    }

    private AssetEvalProcessDTO getAssetEvalProcess(AssetDO parentAsset,List<TreeNode> nodes){
        var count = nodes.stream()
                .map(tn->(AssetEvalProcessDTO)tn.getData())
                .filter(Objects::nonNull)
                .mapToLong(AssetEvalProcessDTO::getCount)
                .sum();
        var finishCount = nodes.stream()
                .map(tn->(AssetEvalProcessDTO)tn.getData())
                .filter(Objects::nonNull)
                .mapToLong(AssetEvalProcessDTO::getFinishCount)
                .sum();
        return AssetEvalProcessDTO.builder()
                .systemId(parentAsset.getSystemId())
                .assetId(parentAsset.getId())
                .assetName(AssetUtils.getAssetName(parentAsset))
                .count(count)
                .finishCount(finishCount)
                .build();
    }

    private FinishState getUnitTargetFinishState(SystemTargetDO targetDO, List<EvaluationRecordDO> evaluationRecords) {
        return evaluationRecords.stream()
                .filter(er -> StringUtils.equals(er.getUnitTargetId(), targetDO.getKmTargetId()))
                .map(EvaluationRecordDO::getFinishState)
                .findFirst()
                .orElse(FinishState.NONE);
    }

    private FinishState getUnitTargetFinishState(SystemSpecialTargetDO targetDO, List<EvaluationRecordDO> evaluationRecords) {
        return evaluationRecords.stream()
                .filter(er -> StringUtils.equals(er.getUnitTargetId(), targetDO.getId()))
                .map(EvaluationRecordDO::getFinishState)
                .findFirst()
                .orElse(FinishState.NONE);
    }
}